package com.gsbelarus.gedemin.lib.ui.dialog;

import android.content.Context;
import android.view.View;
import android.widget.NumberPicker;

import com.afollestad.materialdialogs.MaterialDialog;
import com.gsbelarus.gedemin.lib.R;
import com.gsbelarus.gedemin.lib.Utils;
import com.gsbelarus.gedemin.lib.ui.interfaces.OnDateSet;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

final public class DatePickerDialog {

    private Context context;
    private OnDateSet listener;
    private MaterialDialog dialog;
    private List<Calendar> dates;
    private int choiceMod;

    private int selectedDay;
    private int selectedMonth;
    private int selectedYear = 2014;

    private NumberPicker dayPicker;
    private NumberPicker monthPicker;
    private NumberPicker yearPicker;

    public void setOnDateSetListeners(OnDateSet listener) {
        this.listener = listener;
    }

    /**
     * Конструктор для создания диалога с выбором даты
     *
     * @param context   конетекст приложения
     * @param dates     массив дат, из которых доступен выбор.
     * @param choiceMod отображение определенных полей.
     *                  Calendar.DATE - отображаются все поля.
     *                  Calendar.MONTH - отображаются поля выбора месяца и года.
     *                  Calendar.YEAR - отображается только поле выбора года.
     */
    public DatePickerDialog(Context context, List<Calendar> dates, int choiceMod) {
        this.context = context;
        this.dates = dates;
        this.choiceMod = choiceMod;

        View pickerView = View.inflate(context, R.layout.date_picker, null);

        dialog = new MaterialDialog.Builder(context)
                .title("...")
                .customView(pickerView, false)
                .positiveText(R.string.dialog_ok)
                .negativeText(R.string.dialog_cancel)
                .callback(new MaterialDialog.ButtonCallback() {
                    @Override
                    public void onPositive(MaterialDialog dialog) {
                        super.onPositive(dialog);
                        dialog.cancel();
                        updateDate();
                    }

                    @Override
                    public void onNegative(MaterialDialog dialog) {
                        super.onNegative(dialog);
                        dialog.cancel();
                    }
                }).build();

        dayPicker = (NumberPicker) pickerView.findViewById(R.id.day_picker);
        monthPicker = (NumberPicker) pickerView.findViewById(R.id.month_picker);
        yearPicker = (NumberPicker) pickerView.findViewById(R.id.year_picker);

        dayPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
        monthPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
        yearPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
    }

    /**
     * Показывает диалог с выбором даты
     */
    public void show() {
        switch (choiceMod) {
            case Calendar.DATE:
                dayPicker.setVisibility(View.VISIBLE);
                monthPicker.setVisibility(View.VISIBLE);
                yearPicker.setVisibility(View.VISIBLE);
                break;
            case Calendar.MONTH:
                dayPicker.setVisibility(View.GONE);
                monthPicker.setVisibility(View.VISIBLE);
                yearPicker.setVisibility(View.VISIBLE);
                break;
            case Calendar.YEAR:
                dayPicker.setVisibility(View.GONE);
                monthPicker.setVisibility(View.GONE);
                yearPicker.setVisibility(View.VISIBLE);
                break;
        }
        initYearPicker();
        dialog.show();
        updateTitleText();
    }

    public void cancel() {
        dialog.cancel();
    }

    public void setDate(int day, int month, int year) {
        Calendar cal = Calendar.getInstance();
        cal.set(year, month - 1, day);
        setDate(cal);
    }

    public void setDate(Calendar c) {
        for (int i = 1; i < dates.size(); i++) {

            Utils.initCalendarFields(c, choiceMod, 1);
            Utils.initCalendarFields(dates.get(i), choiceMod, 1);

            if (c.compareTo(dates.get(i)) <= 0) {
                if (c.compareTo(dates.get(i)) == 0)
                    initDate(dates.get(i));
                else
                    initDate(dates.get(i - 1));
                break;

            } else if (i == dates.size() - 1)
                initDate(dates.get(i));
        }
    }

    private void initDate(Calendar cal) {
        selectedDay = cal.get(Calendar.DAY_OF_MONTH);
        selectedMonth = cal.get(Calendar.MONTH);
        selectedYear = cal.get(Calendar.YEAR);
    }

    /**
     * Заполнение numberPicker (день) значениями из БД за определенный месяц и год.
     * Назначение минимального и максимального индекса.
     * Запрет на бесконечную прокрутку.
     * Установка changeListner (при прокрутке списка метод onValueChange
     * инициализирует переменную selectedDay).
     */
    private void initDayPicker(final List<Integer> days) {
        dayPicker.invalidate();
        dayPicker.setDisplayedValues(null);
        dayPicker.setMinValue(0);
        dayPicker.setMaxValue(days.size() - 1);
        dayPicker.setWrapSelectorWheel(false);
        dayPicker.setDisplayedValues(convertIntegerIntoString(days));
        dayPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
            @Override
            public void onValueChange(NumberPicker numberPicker, int lastId, int currentId) {
                selectedDay = days.get(currentId);
                updateTitleText();
            }
        });
        dayPicker.setValue(days.indexOf(selectedDay));
        selectedDay = days.get(dayPicker.getValue());
    }

    /**
     * Заполнение numberPicker (месяц) значениями из БД за определенный год.
     * Назначение минимального и максимального индекса.
     * Запрет на бесконечную прокрутку.
     * Установка changeListner (при прокрутке списка метод onValueChange
     * инициализирует переменную selectedMonth и перерисовывает dayPicker).
     */
    private void initMonthPicker(final List<Integer> months, final int year) {
        monthPicker.invalidate();
        monthPicker.setDisplayedValues(null);
        monthPicker.setMinValue(0);
        monthPicker.setMaxValue(months.size() - 1);
        monthPicker.setWrapSelectorWheel(false);
        monthPicker.setDisplayedValues(convertMonthIdIntoNames(months));
        monthPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
            @Override
            public void onValueChange(NumberPicker numberPicker, int lastId, int currentId) {
                selectedMonth = months.get(currentId);
                if (choiceMod == Calendar.DATE)
                    initDayPicker(getDays(selectedMonth, year));
                updateTitleText();
            }
        });
        monthPicker.setValue(months.indexOf(selectedMonth));
        selectedMonth = months.get(monthPicker.getValue());

        if (choiceMod == Calendar.DATE)
            initDayPicker(getDays(selectedMonth, year));
    }

    /**
     * Заполнение numberPicker (год) значениями из БД
     * Назначение минимального и максимального индекса.
     * Запрет на бесконечную прокрутку.
     * Установка changeListner (при прокрутке списка метод onValueChange
     * инициализирует переменную selectedYear и перерисовывает monthPicker).
     */
    private void initYearPicker() {
        final List<Integer> years = getYear();

        monthPicker.invalidate();
        monthPicker.setDisplayedValues(null);
        yearPicker.setMinValue(0);
        yearPicker.setMaxValue(years.size() - 1);
        yearPicker.setWrapSelectorWheel(false);
        yearPicker.setDisplayedValues(convertIntegerIntoString(years));
        yearPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
            @Override
            public void onValueChange(NumberPicker numberPicker, int lastId, int currentId) {
                selectedYear = years.get(currentId);
                if (choiceMod == Calendar.DATE || choiceMod == Calendar.MONTH)
                    initMonthPicker(getMonth(selectedYear), selectedYear);
                updateTitleText();
            }
        });
        yearPicker.setValue(years.indexOf(selectedYear));
        selectedYear = years.get(yearPicker.getValue());

        if (choiceMod == Calendar.DATE || choiceMod == Calendar.MONTH)
            initMonthPicker(getMonth(selectedYear), selectedYear);
    }

    /**
     * Делает выборку дней по месяцу и году
     */
    public List<Integer> getDays(int month, int year) {
        List<Integer> days = new ArrayList<Integer>();
        for (Calendar date : dates) {
            if (date.get(Calendar.YEAR) == year && date.get(Calendar.MONTH) == month) {
                if (days.isEmpty())
                    days.add(date.get(Calendar.DAY_OF_MONTH));
                if (days.get(days.size() - 1) != date.get(Calendar.DAY_OF_MONTH))
                    days.add(date.get(Calendar.DAY_OF_MONTH));

            } else if (!days.isEmpty())
                break;
        }
        return days;
    }

    /**
     * Делает выборку месяцев по году
     */
    public List<Integer> getMonth(int year) {
        List<Integer> months = new ArrayList<Integer>();
        for (Calendar date : dates) {
            if (date.get(Calendar.YEAR) == year) {
                if (months.isEmpty())
                    months.add(date.get(Calendar.MONTH));
                if (months.get(months.size() - 1) != date.get(Calendar.MONTH))
                    months.add(date.get(Calendar.MONTH));

            } else if (!months.isEmpty())
                break;
        }
        return months;
    }

    /**
     * Делает выборку годов
     */
    private List<Integer> getYear() {
        List<Integer> years = new ArrayList<Integer>();
        years.add(dates.get(0).get(Calendar.YEAR));
        for (Calendar date : dates) {
            if (years.get(years.size() - 1) != date.get(Calendar.YEAR)) {
                years.add(date.get(Calendar.YEAR));
            }
        }
        return years;
    }

    private void updateTitleText() {
        switch (choiceMod) {
            case Calendar.DATE:
                dialog.setTitle(selectedDay + " " + Utils.getMonthName(context, selectedMonth) + " " + selectedYear);
                break;
            case Calendar.MONTH:
                dialog.setTitle(Utils.getMonthName(context, selectedMonth) + " " + selectedYear);
                break;
            case Calendar.YEAR:
                dialog.setTitle(selectedYear + " ");
                break;
        }
    }

    /**
     * Оповещение слушателей об изменении даты
     */
    public void updateDate() {
        listener.updateDate(selectedDay, selectedMonth + 1, selectedYear);
    }

    /**
     * Преобразует номера месяцев (0-11) в полные локализованные названия (из string.xml)
     *
     * @return массив месяцев с полными названиями
     */
    private String[] convertMonthIdIntoNames(List<Integer> months) {
        String[] allNames = context.getResources().getStringArray(R.array.short_month_list_values);
        String[] resultNames = new String[months.size()];
        for (int i = 0; i < months.size(); i++)
            resultNames[i] = allNames[months.get(i)];
        return resultNames;
    }

    private String[] convertIntegerIntoString(List<Integer> integers) {
        String[] resultYears = new String[integers.size()];
        int j = 0;
        for (Integer i : integers) {
            resultYears[j] = String.valueOf(i);
            j++;
        }
        return resultYears;
    }

    public void setDates(List<Calendar> dates) {
        this.dates = dates;
    }

    public List<Calendar> getDates() {
        return dates;
    }
}
